#include "StdAfx.h"
#include "Elevator.h"
#include <algorithm>

CElevator::CElevator(const FloorsCollection& inputFloors, size_t capacity) 
	: m_capacity(capacity), m_curFloor(0), m_steps(0), m_upDirection(true)
{
	m_floors = inputFloors;
	for (FloorsCollection::iterator it = m_floors.begin(); it != m_floors.end(); ++it)
	{
		CFloor* curFloor = *it;
		curFloor->RegisterListeners(
			new CIncomingPassengersListener(*this), 
			new CallRequestlistener(*this)
		);
	}
}

CElevator::~CElevator(void)
{
}

void CElevator::MoveUp()
{
	if(m_curFloor < m_floors.size() - 1)
	{
		++m_curFloor;
		m_upDirection = true;
		++m_steps;
	}
}

void CElevator::MoveDown()
{
	if(m_curFloor > 0)
	{
		--m_curFloor;
		m_upDirection = false;
		++m_steps;
	}
}

void CElevator::StartMove()
{
	CallRequest* curCall = m_currentCallRequests.front();
	if (m_curFloor > curCall->fromFloor)
		MoveDown();
	else if (m_curFloor < curCall->fromFloor)
		MoveUp();

	UpdateCalls(); // if elevator has call-request from this floor to a right direction the request is completed

	UnloadPassengers(); // arrived passengers are trying to exit
	
	m_floors[m_curFloor]->ElevatorArrived(); //incoming passengers are trying to enter
}

void CElevator::UnloadPassengers()
{
	for (PassengerCollection::iterator it = m_passengers.begin(); it != m_passengers.end(); ++it)
		(*it)->NewFloor(m_curFloor); //CPassengerArrivedListener should be notified here

	for (PassengerCollection::iterator it = m_passengerToExit.begin(); it != m_passengerToExit.end(); ++it)
	{
		delete *it;// TODO: maybe someonw else should kill the passenger?
		m_passengers.erase(it);
	}

	m_passengerToExit.clear();
}

void CElevator::UpdateCalls()
{
	//updating current calls registry and taking some of incoming into work
	bool found = false;
	for(CallRequestRegistry::const_iterator it = m_currentCallRequests.begin(); it != m_currentCallRequests.end(); ++it)
	{
		CallRequest* req = *it;
		found = (req->fromFloor == m_curFloor) && (req->upDirection == m_upDirection);
		if (found)
		{
			delete req;
			m_currentCallRequests.erase(it);
			break;
		}
	}

	if (!found)
		return;

	CallRequestRegistry::const_iterator incomingRequestIt = m_incomingCallRequests.begin();
	if (incomingRequestIt == m_incomingCallRequests.end())
		return;// no incoming calls

	m_currentCallRequests.push_back(*incomingRequestIt);
	m_incomingCallRequests.erase(incomingRequestIt);
}
/////////////////////////////////////////////////////////////////////////////////////////////////
void CElevator::CPassengerArrivedListener::OnPassengerArrived(CPassenger* passenger)
{
	PassengerCollection::iterator it = std::find(m_elevator.m_passengers.begin(), m_elevator.m_passengers.end(), passenger);
	if (it != m_elevator.m_passengers.end())
		m_elevator.m_passengerToExit.push_back(*it);
}
/////////////////////////////////////////////////////////////////////////////////////////////////
void CElevator::CIncomingPassengersListener::LoadPassengersAtArival(PassengerCollection& incomingPassengers)
{
	for (PassengerCollection::iterator it = incomingPassengers.begin(); it != incomingPassengers.end(); ++it)
	{
		if (m_elevator.m_capacity == m_elevator.m_passengers.size())
			break;

		CPassenger* p = static_cast<CPassenger*>(*it);
		p->RegisterListener(new CPassengerArrivedListener(m_elevator));
		incomingPassengers.erase(it);
		++m_elevator.m_steps;
	}
}
/////////////////////////////////////////////////////////////////////////////////////////////////
void CElevator::CallRequestlistener::OnCallRequest(CallRequest* callRequest)
{
	if (m_elevator.m_currentCallRequests.empty())
	{
		m_elevator.m_incomingCallRequests.push_back(callRequest);
		m_elevator.m_curFloor = callRequest->fromFloor;
		m_elevator.StartMove();
	}
	else
		m_elevator.m_incomingCallRequests.push_back(callRequest);
}